//=================================================================================================
// DESC: commands for parallel programming (ATMEL AVR)
// AUTHOR: wiRe (http://w1r3.de/)
// DATE: 07/04/2006
//
// Copyright (c)2006 wiRe
//=================================================================================================
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "pprog.h"


//-------------------------------------------------------------------------------------------------
// PIN DECLARATIONS
//-------------------------------------------------------------------------------------------------
#define DATA_OUT_CLK  1
#define DATA_OUT_SD   14

#define DATA_IN_CLK   17
#define DATA_IN_LOAD  16
#define DATA_IN_SD    15

#define READY         10

#define XTAL1         2
#define XTAL2         8

#define _OUTPUT_EN    3   //INV
#define _WRITE_EN     4   //INV
#define BS1           5
#define BS2           7   //=XA1
#define XA0           6
#define XA1           7   //=BS2

#define RESET_EN      9


//-------------------------------------------------------------------------------------------------
// ERROR MESSAGES
//-------------------------------------------------------------------------------------------------
#define PPERROR_PPORT_INIT_FAILED "failed to init parallel port! check administrative rights?"
#define PPERROR_PPROG_INIT_FAILED "failed to init programmer!"




//-------------------------------------------------------------------------------------------------
void PProg::init(short baseport)
{
  if(!port.init(baseport)) throw PPerror(PPERROR_PPORT_INIT_FAILED);

  /*for(short d=0; d < 8; d++)
  {
    write_data((1 << d)^255);
    if(read_data() != ((1 << d)^255)) throw PPerror(PPERROR_PPROG_INIT_FAILED);
  }*/

  //port.clr(RESET_EN);
  //port.clr(XTAL1);
  //port.clr(_WRITE_EN);
  //port.clr(_OUTPUT_EN);
  //port.clr(BS1);
  //port.clr(XA0);
  //port.clr(XA1);
  //port.clr(XTAL1);

  Sleep(1);
  for(int i=0; i<12; i++) port.toggle(XTAL1);
  Sleep(1);
  port.set(RESET_EN);
  Sleep(1);
  port.set(_WRITE_EN);
  port.set(_OUTPUT_EN);
}


//-------------------------------------------------------------------------------------------------
void PProg::terminate()
{
  if(port.BasePort >= 0) port.clr(RESET_EN);
}


//-------------------------------------------------------------------------------------------------
short PProg::read_data()
{
  short d = 0;

  //port.clr(DATA_IN_CLK);
  port.set(DATA_IN_LOAD);
  wait_for_stable_signal();
  port.clr(DATA_IN_LOAD);

  for(short n=8; n>0; n--)
  {
    wait_for_stable_signal();
    d <<= 1;
    if(port.get(DATA_IN_SD)) d |= 1;

    port.set(DATA_IN_CLK);
    wait_for_stable_signal();
    port.clr(DATA_IN_CLK);
  }

  return d;
}


//-------------------------------------------------------------------------------------------------
void PProg::write_data(short d)
{
  //port.clr(DATA_OUT_CLK);
  
  for(short n=8; n>0; n--)
  {
    port.set(DATA_OUT_SD, ((d&0x80)!=0));
    wait_for_stable_signal();
    d <<= 1;

    port.set(DATA_OUT_CLK);
    wait_for_stable_signal();
    port.clr(DATA_OUT_CLK);
  }

  port.clr(DATA_OUT_SD);
}


//-------------------------------------------------------------------------------------------------
void PProg::cmd(short id)
{
  write_data(id);
  port.set(XA1);
  //port.clr(XA0);
  //port.clr(BS1);
  wait_for_stable_signal();

  port.set(XTAL1);
  wait_for_stable_signal();
  port.clr(XTAL1);

  port.clr(XA1);
}


//-------------------------------------------------------------------------------------------------
void PProg::addr_lo(short addr)
{
  write_data(addr);
  //port.clr(XA1);
  //port.clr(XA0);
  //port.clr(BS1);
  wait_for_stable_signal();

  port.set(XTAL1);
  wait_for_stable_signal();
  port.clr(XTAL1);
}


//-------------------------------------------------------------------------------------------------
void PProg::data_lo(short data)
{
  write_data(data);
  port.set(XA0);
  wait_for_stable_signal();

  port.set(XTAL1);
  wait_for_stable_signal();
  port.clr(XTAL1);

  port.clr(XA0);
}


//-------------------------------------------------------------------------------------------------
short PProg::rx()
{
  write_data(0xFF);
  port.clr(_OUTPUT_EN);
  //port.clr(XA0);
  //port.clr(XA1);
  //port.clr(BS1);
  wait_for_stable_signal();

  short d = read_data();

  port.set(_OUTPUT_EN);
  return(d);
}


//-------------------------------------------------------------------------------------------------
int PProg::read_signature()
{
  cmd(0x08);
  addr_lo(0x00);
  int sign = ((int)rx()) << 0;
  
  cmd(0x08);
  addr_lo(0x01);
  sign |= ((int)rx()) << 8;
  
  cmd(0x08);
  addr_lo(0x02);
  sign |= ((int)rx()) << 16;
  
  return sign;
}


//-------------------------------------------------------------------------------------------------
int PProg::read_fuses()
{
  cmd(0x04);
  int fuses = rx();
  
  cmd(0x04);
  port.set(BS1);
  port.set(BS2);
  fuses |= ((int)rx()) << 8;
  port.clr(BS1);
  port.clr(BS2);

  cmd(0x04);
  port.set(BS2);
  fuses |= ((int)rx()) << 16;
  port.clr(BS2);
  
  return fuses;
}


//-------------------------------------------------------------------------------------------------
short PProg::read_lockbits()
{
  cmd(0x04);
  port.set(BS1);
  short lock = rx();
  port.clr(BS1);  
  return lock;
}


//-------------------------------------------------------------------------------------------------
void PProg::write_fuses(int fuses)
{
  cmd(0x40);
  data_lo( (fuses>>0)&255 );
  wait_for_stable_signal();
  port.clr(_WRITE_EN);
  wait_for_stable_signal();
  port.set(_WRITE_EN);
  wait_while_busy();

  cmd(0x40);
  data_lo( (fuses>>8)&255 );
  port.set(BS1);
  wait_for_stable_signal();
  port.clr(_WRITE_EN);
  wait_for_stable_signal();
  port.set(_WRITE_EN);
  wait_while_busy();
  port.clr(BS1);

  cmd(0x40);
  data_lo( (fuses>>16)&255 );
  port.set(BS2);
  wait_for_stable_signal();
  port.clr(_WRITE_EN);
  wait_for_stable_signal();
  port.set(_WRITE_EN);
  wait_while_busy();
  port.clr(BS2);
}


//-------------------------------------------------------------------------------------------------
void PProg::chip_erase()
{
  cmd(0x80);
  port.set(XA1);
  write_data(0x80);
  wait_for_stable_signal();

  port.set(XTAL1);
  wait_for_stable_signal();
  port.clr(XTAL1);

  port.clr(_WRITE_EN);
  wait_for_stable_signal();
  port.set(_WRITE_EN);
  
  wait_while_busy();

  port.clr(XA1);
}


//-------------------------------------------------------------------------------------------------
void PProg::wait_while_busy()
{
  for(int n=0; !port.get(READY); n++)
  {
    if(n > 5000) throw PPerror("timeout!");
    Sleep(1);
  }
}


//-------------------------------------------------------------------------------------------------
void PProg::wait_for_stable_signal()
{
  //Sleep(1);
}
